/*
 * Python 过程处理用于 plpython
 *
 * src/pl/plpython/plpy_procedure.c
 */

#include "postgres.h"

#include "access/htup_details.h"
#include "access/transam.h"
#include "funcapi.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_procedure.h"
#include "plpython.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/syscache.h"

static HTAB *PLy_procedure_cache = NULL;

static PLyProcedure *fc_PLy_procedure_create(HeapTuple fc_procTup, Oid fc_fn_oid, bool fc_is_trigger);
static bool fc_PLy_procedure_valid(PLyProcedure *fc_proc, HeapTuple fc_procTup);
static char *fc_PLy_procedure_munge_source(const char *fc_name, const char *fc_src);


void init_procedure_caches(void)
{
	HASHCTL		fc_hash_ctl;

	fc_hash_ctl.keysize = sizeof(PLyProcedureKey);
	fc_hash_ctl.entrysize = sizeof(PLyProcedureEntry);
	PLy_procedure_cache = hash_create("PL/Python procedures", 32, &fc_hash_ctl,
									  HASH_ELEM | HASH_BLOBS);
}

/*
 * PLy_procedure_name: 获取指定过程的名称。
 *
 * 注意：这返回 SQL 名称，而不是内部 Python 过程名称
 */
char * PLy_procedure_name(PLyProcedure *fc_proc)
{
	if (fc_proc == NULL)
		return "<unknown procedure>";
	return fc_proc->proname;
}

/*
 * PLy_procedure_get: 返回一个缓存的 PLyProcedure，或创建、存储并
 * 返回一个新的 PLyProcedure。
 *
 * fn_oid 是请求的函数的 OID
 * fn_rel 是 InvalidOid 或该函数触发的关系
 * is_trigger 表示该函数是否是触发函数
 *
 * 需要传递 fn_rel 和 is_trigger 的原因是，当触发函数进行验证时，
 * 我们不知道它将与哪些关系一起使用，因此无法传递合理的 fn_rel。
 */
PLyProcedure *
PLy_procedure_get(Oid fc_fn_oid, Oid fc_fn_rel, bool fc_is_trigger)
{
	bool		fc_use_cache = !(fc_is_trigger && fc_fn_rel == InvalidOid);
	HeapTuple	fc_procTup;
	PLyProcedureKey fc_key;
	PLyProcedureEntry *volatile fc_entry = NULL;
	PLyProcedure *volatile fc_proc = NULL;
	bool		fc_found = false;

	fc_procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_fn_oid));
	if (!HeapTupleIsValid(fc_procTup))
		elog(ERROR, "cache lookup failed for function %u", fc_fn_oid);

	/*
	 * 在缓存中查找函数，除非我们没有必要的信息（例如在验证期间）。
	 * 在这种情况下，我们不会缓存任何内容。
	 */
	if (fc_use_cache)
	{
		fc_key.fn_oid = fc_fn_oid;
		fc_key.fn_rel = fc_fn_rel;
		fc_entry = hash_search(PLy_procedure_cache, &fc_key, HASH_ENTER, &fc_found);
		fc_proc = fc_entry->proc;
	}

	PG_TRY();
	{
		if (!fc_found)
		{
			/* 没有找到，创建一个新的过程 */
			fc_proc = fc_PLy_procedure_create(fc_procTup, fc_fn_oid, fc_is_trigger);
			if (fc_use_cache)
				fc_entry->proc = fc_proc;
		}
		else if (!fc_PLy_procedure_valid(fc_proc, fc_procTup))
		{
			/* 找到了，但它无效，释放并重用缓存条目 */
			fc_entry->proc = NULL;
			if (fc_proc)
				PLy_procedure_delete(fc_proc);
			fc_proc = fc_PLy_procedure_create(fc_procTup, fc_fn_oid, fc_is_trigger);
			fc_entry->proc = fc_proc;
		}
		/* 找到了且有效，可以使用它 */
	}
	PG_CATCH();
	{
		/* 不要在缓存中留下未初始化的条目 */
		if (fc_use_cache)
			hash_search(PLy_procedure_cache, &fc_key, HASH_REMOVE, NULL);
		PG_RE_THROW();
	}
	PG_END_TRY();

	ReleaseSysCache(fc_procTup);

	return fc_proc;
}

/*
 * 创建一个新的 PLyProcedure 结构
 */
static PLyProcedure *
fc_PLy_procedure_create(HeapTuple fc_procTup, Oid fc_fn_oid, bool fc_is_trigger)
{
	char		fc_procName[NAMEDATALEN + 256];
	Form_pg_proc fc_procStruct;
	PLyProcedure *volatile fc_proc;
	MemoryContext fc_cxt;
	MemoryContext fc_oldcxt;
	int			fc_rv;
	char	   *fc_ptr;

	fc_procStruct = (Form_pg_proc) GETSTRUCT(fc_procTup);
	fc_rv = snprintf(fc_procName, sizeof(fc_procName),
				  "__plpython_procedure_%s_%u",
				  NameStr(fc_procStruct->proname),
				  fc_fn_oid);
	if (fc_rv >= sizeof(fc_procName) || fc_rv < 0)
		elog(ERROR, "procedure name would overrun buffer");

	/* 将任何在 Python 中不合法的名称字符替换为 '_' */
	for (fc_ptr = fc_procName; *fc_ptr; fc_ptr++)
	{
		if (!((*fc_ptr >= 'A' && *fc_ptr <= 'Z') ||
			  (*fc_ptr >= 'a' && *fc_ptr <= 'z') ||
			  (*fc_ptr >= '0' && *fc_ptr <= '9')))
			*fc_ptr = '_';
	}

	/* 创建一个所有过程信息将存放的长期上下文 */
	fc_cxt = AllocSetContextCreate(TopMemoryContext,
								"PL/Python function",
								ALLOCSET_DEFAULT_SIZES);

	fc_oldcxt = MemoryContextSwitchTo(fc_cxt);

	fc_proc = (PLyProcedure *) palloc0(sizeof(PLyProcedure));
	fc_proc->mcxt = fc_cxt;

	PG_TRY();
	{
		Datum		fc_protrftypes_datum;
		Datum		fc_prosrcdatum;
		bool		fc_isnull;
		char	   *fc_procSource;
		int			fc_i;

		fc_proc->proname = pstrdup(NameStr(fc_procStruct->proname));
		MemoryContextSetIdentifier(fc_cxt, fc_proc->proname);
		fc_proc->pyname = pstrdup(fc_procName);
		fc_proc->fn_xmin = HeapTupleHeaderGetRawXmin(fc_procTup->t_data);
		fc_proc->fn_tid = fc_procTup->t_self;
		fc_proc->fn_readonly = (fc_procStruct->provolatile != PROVOLATILE_VOLATILE);
		fc_proc->is_setof = fc_procStruct->proretset;
		fc_proc->is_procedure = (fc_procStruct->prokind == PROKIND_PROCEDURE);
		fc_proc->is_trigger = fc_is_trigger;
		fc_proc->src = NULL;
		fc_proc->argnames = NULL;
		fc_proc->args = NULL;
		fc_proc->nargs = 0;
		fc_proc->langid = fc_procStruct->prolang;
		fc_protrftypes_datum = SysCacheGetAttr(PROCOID, fc_procTup,
											Anum_pg_proc_protrftypes,
											&fc_isnull);
		fc_proc->trftypes = fc_isnull ? NIL : oid_array_to_list(fc_protrftypes_datum);
		fc_proc->code = NULL;
		fc_proc->statics = NULL;
		fc_proc->globals = NULL;
		fc_proc->calldepth = 0;
		fc_proc->argstack = NULL;

		/*
		 * 获取返回值输出转换所需的信息，
		 * 但仅在这不是触发器的情况下。
		 */
		if (!fc_is_trigger)
		{
			Oid			fc_rettype = fc_procStruct->prorettype;
			HeapTuple	fc_rvTypeTup;
			Form_pg_type fc_rvTypeStruct;

			fc_rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(fc_rettype));
			if (!HeapTupleIsValid(fc_rvTypeTup))
				elog(ERROR, "cache lookup failed for type %u", fc_rettype);
			fc_rvTypeStruct = (Form_pg_type) GETSTRUCT(fc_rvTypeTup);

			/* 禁止伪类型结果，除了 void 或 record */
			if (fc_rvTypeStruct->typtype == TYPTYPE_PSEUDO)
			{
				if (fc_rettype == VOIDOID ||
					fc_rettype == RECORDOID)
					 /* 可以 */ ;
				else if (fc_rettype == TRIGGEROID || fc_rettype == EVENT_TRIGGEROID)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("trigger functions can only be called as triggers")));
				else
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("PL/Python functions cannot return type %s",
									format_type_be(fc_rettype))));
			}

			/* 为过程结果设置输出函数 */
			PLy_output_setup_func(&fc_proc->result, fc_proc->mcxt,
								  fc_rettype, -1, fc_proc);

			ReleaseSysCache(fc_rvTypeTup);
		}
		else
		{
			/*
			 * 在触发函数中，我们使用 proc->result 和 proc->result_in
			 * 来转换元组，但我们还没有足够的信息来设置它们。
			 * PLy_exec_trigger 会处理它。
			 */
			fc_proc->result.typoid = InvalidOid;
			fc_proc->result_in.typoid = InvalidOid;
		}

		/*
		 * 现在获取所需的信息以进行过程参数的输入转换。
		 * 注意，我们在这里忽略输出参数。
		 * 如果该函数返回 record，则这些 I/O 函数将在函数首次调用时设置。
		 */
		if (fc_procStruct->pronargs)
		{
			Oid		   *fc_types;
			char	  **fc_names,
					   *fc_modes;
			int			fc_pos,
						fc_total;

			/* 从 pg_proc 元组中提取参数类型信息 */
			fc_total = get_func_arg_info(fc_procTup, &fc_types, &fc_names, &fc_modes);

			/* 计算 proc->nargs 中的输入和输入输出参数数量 */
			if (fc_modes == NULL)
				fc_proc->nargs = fc_total;
			else
			{
				/* proc->nargs 在上面初始化为 0 */
				for (fc_i = 0; fc_i < fc_total; fc_i++)
				{
					if (fc_modes[fc_i] != PROARGMODE_OUT &&
						fc_modes[fc_i] != PROARGMODE_TABLE)
						(fc_proc->nargs)++;
				}
			}

			/* 为每个输入参数数据分配数组 */
			fc_proc->argnames = (char **) palloc0(sizeof(char *) * fc_proc->nargs);
			fc_proc->args = (PLyDatumToOb *) palloc0(sizeof(PLyDatumToOb) * fc_proc->nargs);

			for (fc_i = fc_pos = 0; fc_i < fc_total; fc_i++)
			{
				HeapTuple	fc_argTypeTup;
				Form_pg_type fc_argTypeStruct;

				if (fc_modes &&
					(fc_modes[fc_i] == PROARGMODE_OUT ||
					 fc_modes[fc_i] == PROARGMODE_TABLE))
					continue;	/* 跳过 OUT 参数 */

				Assert(fc_types[fc_i] == fc_procStruct->proargtypes.values[fc_pos]);

				fc_argTypeTup = SearchSysCache1(TYPEOID,
											 ObjectIdGetDatum(fc_types[fc_i]));
				if (!HeapTupleIsValid(fc_argTypeTup))
					elog(ERROR, "cache lookup failed for type %u", fc_types[fc_i]);
				fc_argTypeStruct = (Form_pg_type) GETSTRUCT(fc_argTypeTup);

				/* 不允许伪类型参数 */
				if (fc_argTypeStruct->typtype == TYPTYPE_PSEUDO)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("PL/Python functions cannot accept type %s",
									format_type_be(fc_types[fc_i]))));

				/* 设置 I/O 函数信息 */
				PLy_input_setup_func(&fc_proc->args[fc_pos], fc_proc->mcxt,
									 fc_types[fc_i], -1,	/* typmod 未知 */
									 fc_proc);

				/* 获取参数名称 */
				fc_proc->argnames[fc_pos] = fc_names ? pstrdup(fc_names[fc_i]) : NULL;

				ReleaseSysCache(fc_argTypeTup);

				fc_pos++;
			}
		}

		/*
		 * 获取函数的文本。
		 */
		fc_prosrcdatum = SysCacheGetAttr(PROCOID, fc_procTup,
									  Anum_pg_proc_prosrc, &fc_isnull);
		if (fc_isnull)
			elog(ERROR, "null prosrc");
		fc_procSource = TextDatumGetCString(fc_prosrcdatum);

		PLy_procedure_compile(fc_proc, fc_procSource);

		pfree(fc_procSource);
	}
	PG_CATCH();
	{
		MemoryContextSwitchTo(fc_oldcxt);
		PLy_procedure_delete(fc_proc);
		PG_RE_THROW();
	}
	PG_END_TRY();

	MemoryContextSwitchTo(fc_oldcxt);
	return fc_proc;
}

/*
 * 将过程插入到 Python 解释器中
 */
void PLy_procedure_compile(PLyProcedure *fc_proc, const char *fc_src)
{
	PyObject   *fc_crv = NULL;
	char	   *fc_msrc;

	fc_proc->globals = PyDict_Copy(PLy_interp_globals);

	/*
	 * SD 是调用之间保留的私有数据。GD 是所有函数共享的全局数据
	 */
	fc_proc->statics = PyDict_New();
	if (!fc_proc->statics)
		PLy_elog(ERROR, NULL);
	PyDict_SetItemString(fc_proc->globals, "SD", fc_proc->statics);

	/*
	 * 将功能代码插入到解释器中
	 */
	fc_msrc = fc_PLy_procedure_munge_source(fc_proc->pyname, fc_src);
	/* 保存混淆的源代码以备后续包含在回溯中 */
	fc_proc->src = MemoryContextStrdup(fc_proc->mcxt, fc_msrc);
	fc_crv = PyRun_String(fc_msrc, Py_file_input, fc_proc->globals, NULL);
	pfree(fc_msrc);

	if (fc_crv != NULL)
	{
		int			fc_clen;
		char		fc_call[NAMEDATALEN + 256];

		Py_DECREF(fc_crv);

		/*
		 * 编译对函数的调用
		 */
		fc_clen = snprintf(fc_call, sizeof(fc_call), "%s()", fc_proc->pyname);
		if (fc_clen < 0 || fc_clen >= sizeof(fc_call))
			elog(ERROR, "string would overflow buffer");
		fc_proc->code = Py_CompileString(fc_call, "<string>", Py_eval_input);
		if (fc_proc->code != NULL)
			return;
	}

	if (fc_proc->proname)
		PLy_elog(ERROR, "could not compile PL/Python function \"%s\"",
				 fc_proc->proname);
	else
		PLy_elog(ERROR, "could not compile anonymous PL/Python code block");
}

void PLy_procedure_delete(PLyProcedure *fc_proc)
{
	Py_XDECREF(fc_proc->code);
	Py_XDECREF(fc_proc->statics);
	Py_XDECREF(fc_proc->globals);
	MemoryContextDelete(fc_proc->mcxt);
}

/*
 * 决定缓存的 PLyProcedure 结构是否仍然有效
 */
static bool fc_PLy_procedure_valid(PLyProcedure *fc_proc, HeapTuple fc_procTup)
{
	if (fc_proc == NULL)
		return false;

	/* 如果 pg_proc 元组已经改变，则无效 */
	if (!(fc_proc->fn_xmin == HeapTupleHeaderGetRawXmin(fc_procTup->t_data) &&
		  ItemPointerEquals(&fc_proc->fn_tid, &fc_procTup->t_self)))
		return false;

	return true;
}

static char * fc_PLy_procedure_munge_source(const char *fc_name, const char *fc_src)
{
	char	   *fc_mrc,
			   *fc_mp;
	const char *fc_sp;
	size_t		fc_mlen;
	int			fc_plen;

	/*
	 * 函数源代码和定义语句的空间
	 */
	fc_mlen = (strlen(fc_src) * 2) + strlen(fc_name) + 16;

	fc_mrc = palloc(fc_mlen);
	fc_plen = snprintf(fc_mrc, fc_mlen, "def %s():\n\t", fc_name);
	Assert(fc_plen >= 0 && fc_plen < fc_mlen);

	fc_sp = fc_src;
	fc_mp = fc_mrc + fc_plen;

	while (*fc_sp != '\0')
	{
		if (*fc_sp == '\r' && *(fc_sp + 1) == '\n')
			fc_sp++;

		if (*fc_sp == '\n' || *fc_sp == '\r')
		{
			*fc_mp++ = '\n';
			*fc_mp++ = '\t';
			fc_sp++;
		}
		else
			*fc_mp++ = *fc_sp++;
	}
	*fc_mp++ = '\n';
	*fc_mp++ = '\n';
	*fc_mp = '\0';

	if (fc_mp > (fc_mrc + fc_mlen))
		elog(FATAL, "buffer overrun in PLy_procedure_munge_source");

	return fc_mrc;
}
